#include <gtest/gtest.h>
#include <gflags/gflags.h>
#include <thread>
#include <chrono>

#include "cache_service.hpp"
#include "data_redis.hpp"
#include "utils.hpp"

DEFINE_string(cache_redis_host, "127.0.0.1", "Cache 测试 Redis 主机地址");
DEFINE_int32(cache_redis_port, 6379, "Cache 测试 Redis 端口");
DEFINE_int32(cache_redis_db, 0, "Cache 测试 Redis 数据库编号");
DEFINE_bool(cache_redis_keep_alive, true, "Cache 测试 Redis 保活选项");

namespace
{
class CacheServiceTest : public ::testing::Test
{
protected:
    static void SetUpTestSuite()
    {
        _redis_client = suye::RedisClientFactory::create(FLAGS_cache_redis_host,
                                                          FLAGS_cache_redis_port,
                                                          FLAGS_cache_redis_db,
                                                          FLAGS_cache_redis_keep_alive);
        ASSERT_TRUE(_redis_client);
        _cache_service = std::make_shared<suye::CacheService>(_redis_client, std::chrono::seconds(60));
    }

    static void TearDownTestSuite()
    {
        _cache_service.reset();
        _redis_client.reset();
    }

    void SetUp() override
    {
        if (_redis_client)
        {
            try
            {
                _redis_client->flushdb();
            }
            catch (const std::exception& e)
            {
                // 忽略清理失败
            }
        }
    }

    static std::shared_ptr<sw::redis::Redis> _redis_client;
    static std::shared_ptr<suye::CacheService> _cache_service;
};

std::shared_ptr<sw::redis::Redis> CacheServiceTest::_redis_client;
std::shared_ptr<suye::CacheService> CacheServiceTest::_cache_service;
} // namespace

TEST_F(CacheServiceTest, GetWithFallbackHit)
{
    const std::string key = "test:user:123";
    const std::string expected_value = "cached_data";

    ASSERT_TRUE(CacheServiceTest::_cache_service->set(key, expected_value, std::chrono::seconds(10)));

    std::string value;
    bool fetcher_called = false;
    auto fetcher = [&fetcher_called](const std::string&, std::string&) {
        fetcher_called = true;
        return false;
    };

    ASSERT_TRUE(CacheServiceTest::_cache_service->get_with_fallback(key, value, fetcher));
    EXPECT_EQ(value, expected_value);
    EXPECT_FALSE(fetcher_called);
}

TEST_F(CacheServiceTest, GetWithFallbackMiss)
{
    const std::string key = "test:user:456";
    const std::string fetched_value = "fetched_from_db";

    std::string value;
    auto fetcher = [&fetched_value](const std::string& k, std::string& v) {
        v = fetched_value;
        return true;
    };

    ASSERT_TRUE(CacheServiceTest::_cache_service->get_with_fallback(key, value, fetcher));
    EXPECT_EQ(value, fetched_value);

    auto cached = CacheServiceTest::_redis_client->get(key);
    ASSERT_TRUE(cached);
    EXPECT_EQ(*cached, fetched_value);
}

TEST_F(CacheServiceTest, Invalidate)
{
    const std::string key = "test:user:789";
    ASSERT_TRUE(CacheServiceTest::_cache_service->set(key, "data", std::chrono::seconds(10)));

    auto before_del = CacheServiceTest::_redis_client->get(key);
    ASSERT_TRUE(before_del);

    ASSERT_TRUE(CacheServiceTest::_cache_service->invalidate(key));

    auto after_del = CacheServiceTest::_redis_client->get(key);
    EXPECT_FALSE(after_del);
}

TEST_F(CacheServiceTest, DelayedDoubleDelete)
{
    const std::string key = "test:user:delay";
    ASSERT_TRUE(CacheServiceTest::_cache_service->set(key, "original", std::chrono::seconds(10)));

    CacheServiceTest::_cache_service->delayed_double_delete(key, std::chrono::milliseconds(200));

    auto immediately_after = CacheServiceTest::_redis_client->get(key);
    EXPECT_FALSE(immediately_after);

    ASSERT_TRUE(CacheServiceTest::_cache_service->set(key, "dirty_write", std::chrono::seconds(10)));

    std::this_thread::sleep_for(std::chrono::milliseconds(300));

    auto after_delay = CacheServiceTest::_redis_client->get(key);
    EXPECT_FALSE(after_delay);
}

TEST_F(CacheServiceTest, InvalidatePattern)
{
    ASSERT_TRUE(CacheServiceTest::_cache_service->set("user:profile:1", "u1", std::chrono::seconds(10)));
    ASSERT_TRUE(CacheServiceTest::_cache_service->set("user:profile:2", "u2", std::chrono::seconds(10)));
    ASSERT_TRUE(CacheServiceTest::_cache_service->set("user:session:1", "s1", std::chrono::seconds(10)));

    auto deleted = CacheServiceTest::_cache_service->invalidate_pattern("user:profile:*");
    EXPECT_GE(deleted, 2u);

    auto u1 = CacheServiceTest::_redis_client->get("user:profile:1");
    EXPECT_FALSE(u1);
    auto s1 = CacheServiceTest::_redis_client->get("user:session:1");
    EXPECT_TRUE(s1);
}

int main(int argc, char* argv[])
{
    google::ParseCommandLineFlags(&argc, &argv, true);
    suye::Init_logger(false, "", 0);
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}


